<?php
/*------------------------------------------------------------------------------
Copyright (C) 2006 J.Ducastel fredbird@fredbird.org

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
------------------------------------------------------------------------------*/
pathnode::heritage('inifilenode',20);
/**
*
* @author J.Ducastel <fredbird@fredbird.org>
* @version 
*/
class iniFileNode extends textFileNode {
/*------------------------------------------------------------------------------
                                                                     constructor
------------------------------------------------------------------------------*/
	/**
	*
	* /
	function iniFileNode($path) {
		$this->__construct($path);
	}/**/
	/**
	*
	* @access
	* /
	function __construct($path) {
		parent::__construct($path);
		$this->_read();
	}/**/
/*------------------------------------------------------------------------------
                                                                  public methods
------------------------------------------------------------------------------*/
	/**
	* sets some data within the cache
	* @return bool
	* @todo sync check
	* @todo check keys formats
	*/
	function setData($section, $param=null, $value=null) {
		// reading if !sync
		if (!$this->_sync) $this->fileToCache();
		switch (true) {
			case $section and $param:
				$this->sections[$section][$param]=$value;
				$this->_sync=false;
				return true;
			case $section:
				// setting a whole section at once
				$this->sections[$section]=$value;
				$this->_sync=false;
				return true;
			case !$section and $param:
				// setting a root param
				$this->params[$param]=$value;
				$this->_sync=false;
			default:
				return false;
		}
	}
	/**
	* return partial or complete data.
	* @param string $section
	* @param string $param
	* @todo sync check 
	* @return mixed targeted data
	*/
	function getData($section=null,$param=null) {
		// reading if !sync
		if (!$this->_sync) $this->fileToCache();
		switch (true) {
			case $section and $param:
				if (!isset($this->sections[$section][$param]))
					return null;
				return $this->sections[$section][$param];
			case $section:
				// getting a whole section at once
				if (!isset($this->sections[$section]))
					return null;
				return $this->sections[$section];
			case $param:
				// getting a root param
				if (!isset($this->params[$param]))
					return null;
				return $this->params[$param];
			default:
				return array_merge($this->params,$this->sections);
				return false;
		}
	}
	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function getComment($section=null,$param=null) {
		
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function setComment($section=null,$param=null,$comment) {
		
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	*/
	function load() {
		return $this->fileToCache();
	}/**/
	
	/**
	* 
	* @param
	* @access public
	* @return 
	*/
	function save() {
		return $this->cacheToFile();
	}/**/

	
	/**
	* 
	* @param
	* @access public
	* @return 
	* /
	function () {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                      properties
------------------------------------------------------------------------------*/
	/**
	* @var type desc
	*/
	//var $type='inifile';
	//var $content_cache; // content cache
	//var $comments_cache; // content comments cache
	var $_sync=false;
	var $params=array(); // root vars data
	var $sections=array(); // section vars data
	var $file_comment_lines=array(); // root/file comments
	var $params_comment_lines=array(); // root vars comments
	var $sections_comment_lines=array(); // section comments
	var $sections_params_comment_lines=array(); // section vars comments
	//var $_ereg_key='[a-zA-Z0-9_-]+';
	var $_ereg_key='[^ ]+';
	// var $_ereg_param='';
/*------------------------------------------------------------------------------
                                                               protected methods
------------------------------------------------------------------------------*/
	/**
	* detects if path applies to self class.
	* subclasses HAVE to implement this method
	* @param
	* @access public
	* @return bool
	*/
	function selfDetector($path) {
		return ereg('\.ini$',$path)/* and is_file($path)*/;
	}/**/

	/**
	* reads file into cache
	* @param
	* @access protected
	* @return 
	*/
	function fileToCache() {
		if (!$this->exists()) return false;
		// reading file
		$lines=file($this->path);
		// parsing file
		$section=null; $param=null;
		foreach ($lines as $line) {
			if (!$line or trim($line)=='') continue;
			if (ereg('^ *;(.*)',$line,$found)) {
				// comment line
				$comment=trim($found[1]);
				$this->addCommentLineFor($comment,$section,$param);
				continue;
			} else if ($name=$this->parseSectionLine($line)) {
				// section start
				$section=$name; // echo "<br /> section $section";
				$this->sections[$section]=array();
				$param=null;
				//continue
			} else if ($data=$this->parseParamLine($line)) {
				$param=$data['key'];
				// param line
				if (!$section) {
					// root param
					$this->params[$param]=$data['value'];
				} else {
					// param within section
					$this->sections[$section][$param]=$data['value'];
				}
			}
		}
		$this->_sync=true;
		return true;
	}/**/
	
	/**
	* converts cache to a string, and writes it in the ini file
	* @param
	* @access protected
	* @return bool
	*/
	function cacheToFile() {
		$str='';
		// file-level comments
		//$str.=$this->makeCommentLines();
		// cycling root params
		foreach ($this->params as $key => $value) {
			// param line
			$str.=$this->makeParamLine($key,$value);
			// param comments
			//$str.=$this->makeCommentLines(null,$key);
		}
		// cycling sections
		foreach ($this->sections as $section => $content) {
			// section start
			$str.=$this->newlinestring()."[$section]";
			// section-level comments
			//$str.=$this->makeCommentLines($section);
			// sections params
			if (is_array($this->sections[$section])) {
				foreach ($this->sections[$section] as $key => $value) {
					// param line
					$str.=$this->makeParamLine($key,$value);
					// param comments
					//$str.=$this->makeCommentLines($section,$key);
				}
			}
		}
		// echo '<pre>'.$str.'</pre>';
		// writing data
		$this->erase();
		$ok=$this->write($str,0);
		if ($ok) $this->_sync=true;
		return $ok;
	}/**/
	
	/**
	* 
	* @param
	* @access protected
	* @return 
	* /
	function () {
		
	}/**/
/*------------------------------------------------------------------------------
                                                                 private methods
------------------------------------------------------------------------------*/
	/**
	* 
	* @param
	* @access private
	* @return 
	*/
	function parseSectionLine($line) {
		if (!ereg('\[('.$this->_ereg_key.')\]',$line,$found))
			return false;
		return $found[1];
	}/**/
	
	/**
	* 
	* @param
	* @access private
	* @return 
	*/
	function parseParamLine($line) {
		$line=trim($line);
		// echo "<p>parsing $line</p>";
		if (!ereg('('.$this->_ereg_key.') *= *(.*)$',$line,$found)) {
			trigger_error("wrong ini param line $line");
			return false;
		}
		//$d=&wak::getservice('wakdebug');
		//echo $d->vardump($found);
		$key=$found[1];
		$value=$found[2]; //echo $d->vardump($value);
		switch (true) {
			case strtolower($value)=='true':
				$value=true;
				break;
			case strtolower($value)=='false':
				$value=false;
				break;
			case strtolower($value)=='null':
				$value=null;
				break;
			case ereg('^[0-9]+$',$value): // integer value
				$value=intval($value);
				break;
			//case ereg
			case ereg('^"(.*)"$',$value,$found): // simple quoted string value
				$value=strval($found[1]);
				break;
			default:
				$value=strval($value);
				break;
		}
		return array('key'=>$key,'value'=>$value);
	}/**/
	
	/**
	* @access private
	* @param $key
	* @param $value
	* @return string
	*/
	function makeParamLine($key, $value) {
		$str=$this->newlinestring().$key.' = ';
		switch (true) {
			case is_bool($value) and $value:
				$str.='true';
				break;
			case is_bool($value) and !$value:
				$str.='false';
				break;
			case is_null($value):
				$str.='null';
				break;
			case is_numeric($value):
				$str.=strval($value);
				break;
			default:
				$str.='"'.strval($value).'"';
		}
		return $str;
	}
	
	/**
	* return comment lines for file, section, or param
	*/
	function makeCommentLines($section=null, $param=null) {
		$str='';
		switch (true) {
			case $section and $param and isset($this->sections_params_comment_lines[$section][$param]):
				$array=$this->sections_params_comment_lines[$section][$param];
				break;
			case $param and !$section and isset($this->params_comment_lines[$param]):
				$array=$this->params_comment_lines[$param];
				break;
			case $section and !$param and isset($this->sections_comment_lines[$section]):
				$array=$this->sections_comment_lines[$section];
				break;
			case !$section and !$param:
				$array=$this->file_comment_lines;
				break;
			default:
				$array=array();
		}
		//$d=wak::getservice('wakdebug');
		//if ($d->haswrongtype($array,'array')) return $str;
		foreach ($array as $line)
			$str.=$this->newlinestring().'; '.$line;
		return $str;
	}
	
	/**
	* adds a comment line for file, section, and/or param
	*/
	function addCommentLineFor($comment, $section=null, $param=null) {
		if (!$section and !$param) {
			// file-level
			$this->file_comment_lines[]=$comment;
		} else if (!$section) {
			// param-level
			$this->params_comment_lines[$param][]=$comment;
		} else if (!$param) {
			// section-level
			$this->sections_comment_lines[$section][]=$comment;
		} else {
			// section+param level
			$this->sections_params_comment_lines[$section][$param][]=$comment;
		}
		return true;
	}
	
}
?>
